package com.fast.commons.shiro;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.fast.commons.utils.StringUtils;
import com.fast.manager.base.domain.Role;
import com.fast.manager.base.domain.RoleMenu;
import com.fast.manager.base.domain.User;
import com.fast.manager.base.repository.UserMapper;
import com.fast.manager.system.domain.Menu;
import com.fast.manager.system.domain.Permit;
import com.fast.manager.system.repository.MenuMapper;
import com.fast.manager.system.repository.PermitMapper;

public class ShiroAuthRealm extends AuthorizingRealm {
	
	private static final Logger logger = LoggerFactory.getLogger(ShiroAuthRealm.class) ;
	
	//注入service的话，会导致该service的缓存失效，事务失效。暂时只能注入DAO
	
	@Autowired
	private UserMapper userMapper ;
	
	@Autowired
	private MenuMapper menuMapper;
	
	@Autowired
	private PermitMapper permitMapper;
	
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		//System.out.println("验证当前Subject时获取到token为：" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
		
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; 
		String account = usernamePasswordToken.getUsername(); 					//账号
		
		User user = this.userMapper.findByAccount(account) ;
		
		if (user == null) {
			throw new UnknownAccountException(); 	//没找到帐号
		} else if (user.getLocked() == -1) { 		//账号被锁定
			throw new DisabledAccountException();
		}
		
		return new SimpleAuthenticationInfo(
				account, 
				user.getPassword(), 
				ByteSource.Util.bytes(user.getCredentialsSalt()), 
				getName());
	}
	
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		List<String> roles = new ArrayList<String>();   
		List<String> permissions = new ArrayList<String>();
		String account = (String) super.getAvailablePrincipal(principals) ; //账号
		
		User user = this.userMapper.findByAccountLinkRole(account) ;
		
		//角色
		List<Role> userRole = user.getRoles() ;
		for (Role role : userRole) {
			roles.add(role.getCode()) ;
		}
		
		//权限
		Map<Long, String> rmMap = new HashMap<Long, String>();
		//菜单去重
		List<RoleMenu> r = user.getRolesMenu() ;
		for (RoleMenu roleMenu : r) {
			String pids = roleMenu.getPermit_ids() ;
			if(null != rmMap.get(roleMenu.getMenu_id())) {
				pids += ","+rmMap.get(roleMenu.getMenu_id()) ;
			}
			rmMap.put(roleMenu.getMenu_id(), pids) ;
		}
		
		for (Long menuId : rmMap.keySet()) {
			//操作权限去重
			String[] permitIds = StringUtils.array_unique(rmMap.get(menuId).toString().split(",")) ;
			
			if(null != permitIds && permitIds.length > 0) {
				Menu menu = this.menuMapper.getMenuById(menuId);
				
				List<Permit> permits = this.permitMapper.getPermits(permitIds);
				for (Permit permit : permits) {
					permissions.add(menu.getCode()+":"+permit.getIdentity()) ;
				}
			}
			
		}
		
		logger.debug("账号：{}", account);
		logger.debug("角色：{}", roles);
		logger.debug("权限：{}", permissions);
		
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
		authorizationInfo.addRoles(roles);  
		authorizationInfo.addStringPermissions(permissions);  
		return authorizationInfo;
	}


	@Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }
}
